package quake.dao;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

import com.amazonaws.services.simpledb.model.Attribute;
import com.amazonaws.services.simpledb.model.Item;
import com.amazonaws.services.simpledb.model.SelectRequest;
import com.amazonaws.services.simpledb.model.SelectResult;

import quake.responses.Tweet;

public class TweetDao extends AbstractDao {
    private static final SimpleDateFormat FULL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private Calendar uct2jstCal;

    public TweetDao(String accessKey, String secretKey) {
        super(accessKey, secretKey);
        uct2jstCal = Calendar.getInstance();
    }

    public List<Tweet> listLastHour() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - 1);

        Locale loc = Locale.getDefault();
        if (loc.equals(Locale.JAPAN)) {
            c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - 9);
        }

        final String todaySelectExpression =
            "select * from " + nowDomain() +
            " where " + ATTRIBUTES_APPROX_TIME + " >= '" + FULL_DATE_FORMAT.format(c.getTime()) + "'";
        List<Tweet> l = listTweets(todaySelectExpression);

        final String yestadaySelectExpression =
            "select * from " + yesterdayDomain() +
            " where " + ATTRIBUTES_APPROX_TIME + " >= '" + FULL_DATE_FORMAT.format(c.getTime()) + "'";
        l.addAll(listTweets(yestadaySelectExpression));

        return l;
    }

    public List<Tweet> listHashTag(String hashtags) {
        final String todaySelectExpression =
            "select * from " + nowDomain() +
            " where " + ATTRIBUTES_HASHTAGS + " = '" + hashtags + "'";
        List<Tweet> l = listTweets(todaySelectExpression);

        final String yestadaySelectExpression =
            "select * from " + yesterdayDomain() +
            " where " + ATTRIBUTES_HASHTAGS + " = '" + hashtags + "'";
        l.addAll(listTweets(yestadaySelectExpression));

        return l;
    }

    public List<Tweet> listUser(String user) {
        final String todaySelectExpression =
            "select * from " + nowDomain() +
            " where " + ATTRIBUTES_THE_USER + " = '" + user + "'";
        List<Tweet> l = listTweets(todaySelectExpression);

        final String yestadaySelectExpression =
            "select * from " + yesterdayDomain() +
            " where " + ATTRIBUTES_THE_USER + " = '" + user + "'";
        l.addAll(listTweets(yestadaySelectExpression));

        return l;
    }

    public List<Tweet> listAndFreeSearch(int hour, List<String> words) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - hour);

        Locale loc = Locale.getDefault();
        if (loc.equals(Locale.JAPAN)) {
            c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - 9);
        }

        StringBuilder todaySelectExpression = new StringBuilder();
        todaySelectExpression.append("select * from ").append(nowDomain())
                             .append(" where ")
                             .append(ATTRIBUTES_APPROX_TIME).append(" >= '").append(FULL_DATE_FORMAT.format(c.getTime())).append("'");

        for (String s : words) {
            todaySelectExpression.append(" and ")
                                 .append(ATTRIBUTES_THE_TWEET)
                                 .append(" like '%")
                                 .append(s)
                                 .append("%'");

        }
        List<Tweet> l = listTweets(todaySelectExpression.toString());

        StringBuilder yestadaySelectExpression = new StringBuilder();
        yestadaySelectExpression.append("select * from ").append(nowDomain())
                                .append(" where ")
                                .append(ATTRIBUTES_APPROX_TIME).append(" >= '").append(FULL_DATE_FORMAT.format(c.getTime())).append("'");

        for (String s : words) {
            yestadaySelectExpression.append(" and ")
                                    .append(ATTRIBUTES_THE_TWEET)
                                    .append(" like '%")
                                    .append(s)
                                    .append("%'");

        }
        l.addAll(listTweets(yestadaySelectExpression.toString()));

        return l;
    }

    public List<Tweet> listOrFreeSearch(int hour, List<String> words) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - hour);

        Locale loc = Locale.getDefault();
        if (loc.equals(Locale.JAPAN)) {
            c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) - 9);
        }

        StringBuilder todaySelectExpression = new StringBuilder();
        todaySelectExpression.append("select * from ").append(nowDomain())
                             .append(" where ")
                             .append(ATTRIBUTES_APPROX_TIME).append(" >= '").append(FULL_DATE_FORMAT.format(c.getTime())).append("'");

        if (!words.isEmpty()) {
            todaySelectExpression.append(" and ");
        }
        for (String s : words) {
            todaySelectExpression.append(ATTRIBUTES_THE_TWEET)
                                 .append(" like '%")
                                 .append(s)
                                 .append("%'")
                                 .append(" or ");

        }
        String sql = todaySelectExpression.toString();
        List<Tweet> l = listTweets(sql.substring(0, sql.length() - 4));

        StringBuilder yestadaySelectExpression = new StringBuilder();
        yestadaySelectExpression.append("select * from ").append(yesterdayDomain())
                                .append(" where ")
                                .append(ATTRIBUTES_APPROX_TIME).append(" >= '").append(FULL_DATE_FORMAT.format(c.getTime())).append("'");

        if (!words.isEmpty()) {
            yestadaySelectExpression.append(" and ");
        }
        for (String s : words) {
            yestadaySelectExpression.append(ATTRIBUTES_THE_TWEET)
                                    .append(" like '%")
                                    .append(s)
                                    .append("%'")
                                    .append(" or ");

        }
        sql = yestadaySelectExpression.toString();
        l.addAll(listTweets(sql.substring(0, sql.length() - 4)));

        return l;
    }

    private List<Tweet> listTweets(final String selectExpression) {
        List<Tweet> l = new ArrayList<Tweet>();

        SelectRequest request = new SelectRequest();
        request.setSelectExpression(selectExpression);

        String nextToken = null;
        do {
            nextToken = invokeSelect(request, l);
            request.setNextToken(nextToken);
        } while(nextToken != null);

        return l;
    }

    private String invokeSelect(SelectRequest request, List<Tweet> l) {
        SelectResult result = service.select(request);
        for (Item item : result.getItems()) {
            Tweet t = new Tweet();
            HashTags h = new HashTags();

            for (Attribute attribute : item.getAttributes()) {
                if (attribute.getName().equals(ATTRIBUTES_THE_USER)) {
                    t.setTheUser(attribute.getValue());
                } else if (attribute.getName().equals(ATTRIBUTES_APPROX_TIME)) {
                    t.setApproxTime(utc2jst(attribute.getValue()));
                } else if (attribute.getName().equals(ATTRIBUTES_THE_TWEET)) {
                    t.setTheTweet(attribute.getValue());
                } else if (attribute.getName().equals(ATTRIBUTES_HASHTAGS)) {
                    h.getHashtags().add(attribute.getValue());
                }
            }

            t.setHashtags(h);
            l.add(t);
        }

        return result.getNextToken();
    }

    private String utc2jst(String value) {
        try {
            uct2jstCal.setTime(FULL_DATE_FORMAT.parse(value));
            uct2jstCal.set(Calendar.HOUR_OF_DAY, uct2jstCal.get(Calendar.HOUR_OF_DAY) + 9);
            return FULL_DATE_FORMAT.format(uct2jstCal.getTime());
        } catch (ParseException e) {
            return value;
        }
    }
}
